package org.angelica.aliyun.manager.timeline;

import com.alicloud.openservices.tablestore.ClientConfiguration;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.AlwaysRetryStrategy;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.filter.CompositeColumnValueFilter;
import com.alicloud.openservices.tablestore.model.filter.SingleColumnValueFilter;
import com.alicloud.openservices.tablestore.timeline.TimelineQueue;
import com.alicloud.openservices.tablestore.timeline.TimelineStore;
import com.alicloud.openservices.tablestore.timeline.TimelineStoreFactory;
import com.alicloud.openservices.tablestore.timeline.core.TimelineStoreFactoryImpl;
import com.alicloud.openservices.tablestore.timeline.model.*;
import com.alicloud.openservices.tablestore.timeline.query.ScanParameter;
import com.google.common.collect.Lists;
import org.angelica.aliyun.entity.StsToken;

import java.util.Iterator;
import java.util.List;

/**
 * 信息流服务
 * @author aizhimin
 */
public class TimelineFeedManager {

    /**
     * 消息ID字段
     */
    public static final String MESSAGE_ID = "message_id";

    private static TimelineFeedManager instance = new TimelineFeedManager();
    private TimelineFeedManager(){ }

    private static StsToken tbStsToken = null;
    private static SyncClient syncClient = null;
    private static TimelineStoreFactory factory = null;
    public static TimelineFeedManager getInstance(String endpoint, String instanceName, StsToken stsToken){
        //token过期，则重新创建SyncClient
        if(tbStsToken != null && !tbStsToken.getSecurityToken().equals(stsToken.getSecurityToken())){
            if(syncClient != null){
                syncClient.shutdown();
            }

            syncClient = null;
            factory = null;
        }

        if(syncClient == null) {
            tbStsToken = stsToken;
            // ClientConfiguration提供了很多配置项，以下只列举部分。
            ClientConfiguration clientConfiguration = new ClientConfiguration();
            // 设置建立连接的超时时间。
            clientConfiguration.setConnectionTimeoutInMillisecond(10000);
            // 设置socket超时时间。
            clientConfiguration.setSocketTimeoutInMillisecond(10000);
            // 设置重试策略，若不设置，采用默认的重试策略。
            clientConfiguration.setRetryStrategy(new AlwaysRetryStrategy());
            syncClient = new SyncClient(endpoint, stsToken.getAccessKeyId(), stsToken.getAccessKeySecret(),
                    instanceName,clientConfiguration,stsToken.getSecurityToken());
            //初始化factory
            factory = new TimelineStoreFactoryImpl(syncClient);
        }
        return instance;
    }

    /**
     * 初始化消息存储库
     * @param idSchema
     * @param prepareTable
     * @return
     */
    public TimelineStore initStoreTable(TimelineIdentifierSchema idSchema,boolean prepareTable) {

        // table used to store messages, with manual set sequence id, long term storage and index.
        TimelineSchema timelineSchema = new TimelineSchema("feed_store", idSchema)
                .autoGenerateSeqId();
        TimelineStore service = factory.createTimelineStore(timelineSchema);
        if (prepareTable) {
            service.prepareTables();
        }
        return service;
    }

    /**
     * 初始化消息同步库
     * @param idSchema
     * @param prepareTable
     * @return
     */
    public TimelineStore initSyncTable(TimelineIdentifierSchema idSchema, boolean prepareTable) {
        // table used for sync messages, with auto generated sequence id and ttl.
        TimelineSchema timelineSchema = new TimelineSchema("feed_sync", idSchema)
                .autoGenerateSeqId();

        TimelineStore service = factory.createTimelineStore(timelineSchema);
        if (prepareTable) {
            service.prepareTables();
        }
        return service;
    }

    /**
     * 发布一条状态
     * @param storeIdentifier
     * @param message
     * @param friendIdentifiers
     * @param storeService
     * @param syncService
     */
    public TimelineEntry posts(TimelineIdentifier storeIdentifier, TimelineMessage message, List<TimelineIdentifier> friendIdentifiers,TimelineStore storeService,TimelineStore syncService){
        //写入自己的存储库
        TimelineQueue userTimelineQueue = storeService.createTimelineQueue(storeIdentifier);
        TimelineEntry timelineEntry = userTimelineQueue.store(message);
        //同步给自己和可见好友，只同步消息ID
        for (TimelineIdentifier identifier: friendIdentifiers) {
            TimelineQueue friendTimelineQueue = syncService.createTimelineQueue(identifier);
            TimelineMessage syncTimelineMessage = new TimelineMessage()
                    .setField(MESSAGE_ID,message.getString(MESSAGE_ID));
            friendTimelineQueue.store(syncTimelineMessage);
        }
        return timelineEntry;
    }

    /**
     * 用户刷新自己的朋友圈。
     * @param timelineIdentifier
     * @param toSequenceId
     * @param syncService
     * @return
     */
    public List<TimelineEntry> refresh(TimelineIdentifier timelineIdentifier, long toSequenceId,TimelineStore syncService) {
        TimelineQueue receiveBox = syncService.createTimelineQueue(timelineIdentifier);
        ScanParameter param = new ScanParameter()
                .scanBackward(Long.MAX_VALUE,toSequenceId)
                .maxCount(10);

        List<TimelineEntry> entryList = Lists.newArrayList();
        Iterator<TimelineEntry> entries = receiveBox.scan(param);
        while (entries.hasNext()) {
            entryList.add(entries.next());
        }
        return entryList;
    }

    /**
     * 根据消息ID批量获取存储库的消息体
     * @param timelineIdentifier
     * @param messageIdList
     * @param storeService
     * @return
     */
    public List<TimelineEntry> listStoreMessage(TimelineIdentifier timelineIdentifier,List<String> messageIdList,TimelineStore storeService){
        TimelineQueue timelineQueue = storeService.createTimelineQueue(timelineIdentifier);

        CompositeColumnValueFilter compositeColumnValueFilter = new CompositeColumnValueFilter(CompositeColumnValueFilter.LogicOperator.OR);
        for(String messageId:messageIdList){
            SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(MESSAGE_ID,
                    SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromString(messageId));
            compositeColumnValueFilter.addFilter(singleColumnValueFilter);
        }
        ScanParameter param = new ScanParameter()
                .scanBackward(Long.MAX_VALUE)
                .withFilter(compositeColumnValueFilter);

        List<TimelineEntry> entryList = Lists.newArrayList();
        Iterator<TimelineEntry> entries =timelineQueue.scan(param);
        while (entries.hasNext()) {
            entryList.add(entries.next());
        }
        return entryList;
    }

    /**
     * 删除同步库中的消息体
     * @param friendIdentifiers
     * @param messageId
     * @param syncService
     */
    public void deleteSyncMessage(List<TimelineIdentifier> friendIdentifiers,String messageId,TimelineStore syncService){
        for (TimelineIdentifier identifier: friendIdentifiers) {
            TimelineQueue friendTimelineQueue = syncService.createTimelineQueue(identifier);
            SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(MESSAGE_ID,
                    SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromString(messageId));
            ScanParameter param = new ScanParameter()
                    .scanBackward(Long.MAX_VALUE)
                    .withFilter(singleColumnValueFilter);
            Iterator<TimelineEntry> entries = friendTimelineQueue.scan(param);
            while (entries.hasNext()) {
                TimelineEntry timelineEntry = entries.next();
                friendTimelineQueue.delete(timelineEntry.getSequenceID());
            }
        }
    }

    /**
     * 根据消息ID获取消息体
     * @param timelineIdentifier
     * @param messageId
     * @param storeService
     * @return
     */
    public TimelineEntry getStoreMessage(TimelineIdentifier timelineIdentifier,String messageId,TimelineStore storeService){
        TimelineQueue timelineQueue = storeService.createTimelineQueue(timelineIdentifier);
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(MESSAGE_ID,
                SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromString(messageId));
        ScanParameter param = new ScanParameter()
                .scanBackward(Long.MAX_VALUE)
                .withFilter(singleColumnValueFilter);
        Iterator<TimelineEntry> entries =timelineQueue.scan(param);
        while (entries.hasNext()) {
            return entries.next();
        }
        return null;
    }

    /**
     * 删除存储库中的某条消息
     * @param timelineIdentifier
     * @param messageId
     * @param storeService
     */
    public void deleteStoreMessage(TimelineIdentifier timelineIdentifier,String messageId,TimelineStore storeService){
        TimelineQueue timelineQueue = storeService.createTimelineQueue(timelineIdentifier);
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(MESSAGE_ID,
                SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromString(messageId));
        ScanParameter param = new ScanParameter()
                .scanBackward(Long.MAX_VALUE)
                .withFilter(singleColumnValueFilter);
        Iterator<TimelineEntry> entries =timelineQueue.scan(param);
        while (entries.hasNext()) {
            TimelineEntry timelineEntry = entries.next();
            timelineQueue.delete(timelineEntry.getSequenceID());
        }
    }

    /**
     * 用户查询自己发布的状态
     * @param timelineIdentifier
     * @param toSequenceId
     * @param storeService
     * @return
     */
    public List<TimelineEntry> listMyPosts(TimelineIdentifier timelineIdentifier, long toSequenceId,TimelineStore storeService){
        TimelineQueue receiveBox = storeService.createTimelineQueue(timelineIdentifier);
        ScanParameter param = new ScanParameter().scanBackward(Long.MAX_VALUE,toSequenceId)
                .maxCount(10);

        List<TimelineEntry> entryList = Lists.newArrayList();
        Iterator<TimelineEntry> entries = receiveBox.scan(param);
        while (entries.hasNext()) {
            entryList.add(entries.next());
        }
        return entryList;
    }


    /**
     *  获取需要同步状态的用户：包含自己与状态可见好友
     * @param currentUserId
     * @param allows
     * @param forbids
     * @return
     */
    public List<String> getFriendsForPost(String currentUserId,List<String> allows, List<String> forbids) {
        //将自己加入同步列表
        allows.add(currentUserId);
        //过滤掉不能查看该状态的用户
        List<String> allUsers = getAllFriends(currentUserId);
        for (String user: allUsers) {
            if (!forbids.contains(user)) {
                allows.add(user);
            }
        }
        return allows;
    }

    /**
     * 获取用户的所有好友
     * @param currentUserId 当前用户ID
     * @return
     */
    public List<String> getAllFriends(String currentUserId) {
        /**
         * 获取朋友列表，这里省略实现。
         * 如果是使用Table Store，则一个getRange就可以查询出所有好友列表。
         */
        return Lists.newArrayList("user_b","user_c","user_d");
    }
}
